package lt.mockos.app.client.cpu.hw;

import java.util.ArrayList;
import java.util.Arrays;

import lt.mockos.app.client.cpu.event.StepEvent;

import com.google.gwt.event.shared.EventBus;

public class Interpreter {

    private RealMachine rm;

    private EventBus eventBus;

    public Interpreter(RealMachine rm, EventBus eventBus) {
        this.rm = rm;
        this.eventBus = eventBus;
    }

    public enum CMD {
        ADD, SUB, MUL, DIV, NEG, CMP, //
        JT, // xy
        JH, // xy
        JE, // xy
        JL, // xy
        HALT, //
        LW, // xy
        SW, // xy
        ATOB, BTOA, //
        PUSH, POP, //
        PRNS, PRNW, SCNW;

        public static ArrayList<CMD> getList() {
            return new ArrayList<CMD>(Arrays.asList(values()));
        }
    }

    public void callStep() {
        callCMD(getNextCommand());
    }

    public void callRun() {
        CMD cmd = getNextCommand();
        while (!cmd.equals(CMD.HALT)) {
            if (cmd.equals(CMD.SCNW)) {
                break;
            } else if (rm.getRegisters().TI == 1) {
                break;
            }
            callCMD(cmd);
            cmd = getNextCommand();
        }
        callCMD(cmd);
    }

    private CMD getNextCommand() {

        ArrayList<CMD> filtered = CMD.getList();
        word = getCodeWord();

        int i = 2;
        while (filtered.size() > 1) {
            if (i == 5) {
                word = getCodeWord();
                i = 0;
            }
            String f = word.substring(0, i);
            filtered = findCMD(f, filtered);
            i++;
        }
        if (filtered.size() == 1) {
            return filtered.get(0);
        } else {
            rm.getRegisters().PI = 2;
            return CMD.HALT;
        }

    }

    private ArrayList<CMD> findCMD(String f, ArrayList<CMD> filtered) {
        ArrayList<CMD> result = new ArrayList<CMD>();
        for (CMD cmd : filtered) {
            if (cmd.name().contains(f)) {
                result.add(cmd);
            }
        }
        return result;
    }

    private int[] getXY() {
        int[] xy = new int[2];
        xy[0] = Integer.valueOf(word.substring(2, 3));
        xy[1] = Integer.valueOf(word.substring(3, 4));
        return xy;
    }

    private String word;

    private String getCodeWord() {
        return rm.getCodeWord();
    }

    private void callCMD(CMD cmd) {
        if (cmd == null) {
            //
        } else {
            int[] xy;
            switch (cmd) {
                case ADD:
                    rm.ADD();
                    break;
                case SUB:
                    rm.SUB();
                    break;
                case MUL:
                    rm.MUL();
                    break;
                case DIV:
                    rm.DIV();
                    break;
                case NEG:
                    rm.NEG();
                    break;
                case CMP:
                    rm.CMP();
                    break;
                case JT:
                    xy = getXY();
                    rm.JT(xy[0], xy[1]);
                    break;
                case JH:
                    xy = getXY();
                    rm.JH(xy[0], xy[1]);
                    break;
                case JE:
                    xy = getXY();
                    rm.JE(xy[0], xy[1]);
                    break;
                case JL:
                    xy = getXY();
                    rm.JL(xy[0], xy[1]);
                    break;
                case HALT:
                    rm.HALT();
                    break;
                case LW:
                    xy = getXY();
                    rm.LW(xy[0], xy[1]);
                    break;
                case SW:
                    xy = getXY();
                    rm.SW(xy[0], xy[1]);
                    break;
                case ATOB:
                    rm.ATOB();
                    break;
                case BTOA:
                    rm.BTOA();
                    break;
                case PUSH:
                    rm.PUSH();
                    break;
                case POP:
                    rm.POP();
                    break; //
                case PRNS:
                    rm.PRINTS();
                    break;
                case PRNW:
                    rm.PRINTW();
                    break;
                case SCNW:
                    rm.SCANW();
                    break;//
                default:
                    break;
            }

        }
        eventBus.fireEvent(new StepEvent(cmd.name()));

    }
}